﻿#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4244)
// 新版控件

#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.\
Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' public\
KeyToken='6595b64144ccf1df' language='*'\"")
// clicker.cpp : 定义应用程序的入口点。
//

#include "clicker.h"
#include "framework.h"

#define MAX_LOADSTRING 100

// 全局变量:
HINSTANCE hInst;                     // 当前实例
WCHAR szTitle[MAX_LOADSTRING];       // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名

// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK TestAdminProc(HWND, UINT, WPARAM, LPARAM);

static double dpi;
static HWND hNotice, g_hWnd;
static bool stopSignal; // 停止信号

BOOL CALLBACK TestAdminProc(HWND hDlg, UINT message, WPARAM wParam,
                            LPARAM lParam);
BOOL CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam,
                           LPARAM lParam);
BOOL CALLBACK AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                      _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine,
                      _In_ int nCmdShow) {
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_MY, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    if (DpiAware(&dpi) != 0)
        dpi = 1; // DPI感知，在Win10及以上系统中适配高分屏

    // 执行应用程序初始化:
    if (!InitInstance(hInstance, nCmdShow)) {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY));

    MSG msg;

    // 主消息循环:
    while (GetMessage(&msg, nullptr, 0, 0)) {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int)msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance) {
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MY);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
    hInst = hInstance; // 将实例句柄存储在全局变量中

    HWND hWnd = CreateWindowW(
        szWindowClass, szTitle,
        WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 200 * dpi,
        200 * dpi, 440 * dpi, 500 * dpi, nullptr, nullptr, hInstance, nullptr);

    if (!hWnd) {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    // 注册全局快捷键
    if (!RegisterHotKey(hWnd, HOTKEY_CLICKING, MOD_SHIFT, VK_F2)) {
        MessageBox(NULL, L"热键注册失败！请尝试以管理员身份重启程序！", L"错误",
                   MB_ICONERROR);
        exit(1);
    }
    if (!RegisterHotKey(hWnd, HOTKEY_HIDE_WINDOW, MOD_CONTROL, 'Q')) {
        MessageBox(NULL, L"热键注册失败！请尝试以管理员身份重启程序！", L"错误",
                   MB_ICONERROR);
        exit(1);
    }
    if (!RegisterHotKey(hWnd, HOTKEY_GET_MOUSE_POS, MOD_CONTROL, 'D')) {
        MessageBox(NULL, L"热键注册失败！请尝试以管理员身份重启程序！", L"错误",
                   MB_ICONERROR);
        exit(1);
    }

    return TRUE;
}

DWORD WINAPI ClickerThread(LPVOID pv) {
    Clicker::ClickerConfig click = *(Clicker::ClickerConfig *)pv;
    int iToBeClicked = click.iTimes;
    bool inf = false;
    stopSignal = false;
    if (click.iTimes <= 0) {
        iToBeClicked = -1;
        inf = true;
    }
    wchar_t tmp[100] = {0};
    int count = 0;

    // 展示信息框
    if (click.show) {
        ShowWindow(hNotice, SW_NORMAL);
    }
    
    int id = click.key.getID();

    if (click.bMoveToPos) {
        SetCursorPos(click.pos.x, click.pos.y);
    }

    while (true) {
        if (click.key.getType() == Clicker::KeyInfo::keyboard) {
            keybd_event(id, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0);
            keybd_event(id, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
        } else { // 区别鼠标和键盘，并根据设定好的map来按下和松开
            if (click.bMoveToPos && click.bLockMouse) {
                SetCursorPos(click.pos.x, click.pos.y); // 锁定位置的话每次都点
            }
            mouse_event(click.key.getMouseEvent().at(0), 0, 0, 0, 0);
            mouse_event(click.key.getMouseEvent().at(1), 0, 0, 0, 0);
        }

        count++;
        wstring pos_tmp = L"";
        if (click.bLockMouse) {
            pos_tmp = L"在固定坐标处";
        }

        wsprintf(tmp, L"%s连点\"%ls\": 已点按%d/%d次", pos_tmp.data(),
                 click.key.getStr().data(), count, click.iTimes);
        if (inf) {
            wsprintf(tmp, L"%s连点\"%ls\": 已点按%d次", pos_tmp.data(),
                     click.key.getStr().data(), count);
        }
        SetWindowText(hNotice, tmp);
        if (!inf) {
            if (count >= click.iTimes) {
                SetWindowText(hNotice, L"连点操作完成");
                break;
            }
        }

        // 被中断
        if (stopSignal == true) {
            SetWindowText(hNotice, L"连点操作中断");
            break;
        }

        if (count >= max_clickTimes) {
            SetWindowText(hNotice, L"已达到最大连点次数(100,000次)，自动中断");
            break;
        }

        Sleep(click.iSleep);
    }
    if (click.show) {
        Sleep(1000);
        ShowWindow(hNotice, SW_HIDE);
    } // 只有显示了信息窗口才会停顿一秒
    SendMessage(g_hWnd, WM_CLICKDONE, 1, 0);
    ExitThread(0);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
                         LPARAM lParam) {
    auto tackleCfgError = [](std::exception &exc) { // 处理配置文件错误：重置
        wstring wsc = Clicker::s2w(exc.what());
        wsc += L"\n\n检测到配置文件异常，这可能是文件被意外修改导致的。\n"
            L"程序将尝试自动恢复默认配置。\n\n"
            L"如重启程序后问题依旧存在，请手动删除配置文件：\n"
            L"1. 关闭程序\n"
            L"2. 删除程序文件夹下的 clicker.json 和 clicker_memory.json\n"
            L"3. 重新启动程序";
        MessageBox(nullptr, wsc.data(), L"连点器 - 程序出现错误", MB_ICONSTOP);
        DeleteFile(L"clicker.json");
        DeleteFile(L"clicker_memory.json");
        exit(114);
        };

    static HFONT hf, hfBold;
    static HWND hChoice, hKey, hEdTimes, hEdSleep, hStart, hStop,
        hSelectKey, hSelectKeyShow, hEdPosX, hEdPosY;
    static HMENU hMenu;

    static DWORD ThreadID;
    static HANDLE hClickerThread;

    static bool bClicking, bSelectingKey,bAutoHide;

    static Clicker::KeyInfo key;
    static Clicker::ClickerConfig click;

    try {
        // 配置文件：配置和记忆
        extern nlohmann::json defMemo;
        extern nlohmann::json defClickerConfig;
        static Clicker::JsonConfig cfg("clicker.json",defClickerConfig);
        static Clicker::JsonConfig memory("clicker_memory.json", defMemo);

        // 加载&保存记忆：使用Lambda表达式，引用捕获所有变量
        auto loadMemory = [&]() {
            // 根据记忆
            int interval = memory.getConfig<int>(L"interval");
            int times = memory.getConfig<int>(L"times");
            int vKeyID = memory.getConfig<int>(L"vkey_id");

            SetWindowText(hEdTimes, std::to_wstring(times).data());
            SetWindowText(hEdSleep, std::to_wstring(interval).data());

            // 模拟发消息：选择键
            bSelectingKey = true;
            SendMessage(hWnd, WM_KEYUP, vKeyID, 0);
            bool bKeep = memory.getConfig<bool>(L"keep");
            if (bKeep)
                SendMessage(hWnd, WM_COMMAND, 1101, 0);
            else
                SendMessage(hWnd, WM_COMMAND, 1102, 0);

            // 模拟发消息：鼠标设置
            bool bMoveTo = memory.getConfig<bool>(L"move_to_pos");
            bool bLockMouse = memory.getConfig<bool>(L"lock_mouse");
            int posX = 0, posY = 0;
            json pos = memory.getConfig<json>(L"mouse_pos");
            Button_SetCheck(GetDlgItem(hWnd, BUTTON_MOUSE_LOCK), bLockMouse);
            Button_SetCheck(GetDlgItem(hWnd, BUTTON_MOVE_TO), bMoveTo);
            SetWindowText(hEdPosX, std::to_wstring(pos["x"].get<int>()).data());
            SetWindowText(hEdPosY, std::to_wstring(pos["y"].get<int>()).data());

            SendMessage(hWnd, WM_COMMAND, BUTTON_MOVE_TO,
                        (LPARAM)GetDlgItem(hWnd, BUTTON_MOVE_TO));
        };
        auto saveMemory = [&]() {
            // 退出时保存记忆
            wchar_t *tmp_str = new wchar_t[100];
            int times_tmp = 100, interval_tmp = 50, vKeyID_tmp = -1;
            bool bKeep = false;
            lstrcpyW(tmp_str, L"\0");
            GetWindowText(hEdTimes, tmp_str, 100);
            if (swscanf(tmp_str, L"%d", &times_tmp) != 1)
                times_tmp = 100;
            lstrcpyW(tmp_str, L"\0");
            GetWindowText(hEdSleep, tmp_str, 100);
            if (swscanf(tmp_str, L"%d", &interval_tmp) != 1)
                interval_tmp = 100;
            delete[] tmp_str;
            bKeep = Button_GetCheck(GetDlgItem(hWnd, 1101));
            vKeyID_tmp = key.getID();

            memory.setConfig<int>(L"times", times_tmp);
            memory.setConfig<int>(L"interval", interval_tmp);
            memory.setConfig<int>(L"vkey_id", vKeyID_tmp);
            memory.setConfig<bool>(L"keep", bKeep);
            // 鼠标设置的记忆
            memory.setConfig<bool>(L"move_to_pos", Button_GetCheck(GetDlgItem(
                                                       hWnd, BUTTON_MOVE_TO)));
            memory.setConfig<bool>(
                L"lock_mouse",
                Button_GetCheck(GetDlgItem(hWnd, BUTTON_MOUSE_LOCK)));
            int posX_tmp = 0, posY_tmp = 0;
            getWindowText_asInt(hEdPosX, &posX_tmp);
            getWindowText_asInt(hEdPosY, &posY_tmp);
            memory.setConfig<json>(L"mouse_pos",
                                   {{"x", posX_tmp}, {"y", posY_tmp}});
            // Json可以这样添加“注释”吗？🤔
            memory.setConfig(
                L".NOTICE",
                L"此Json文件为程序记忆文件，请不要直接编辑！如果你不小"
                L"心编辑这个文件导致程序出错，请删除这个文件。");
            memory.saveConfig();
            return 0;
        };

        auto testForAdmin = []() { // 检验管理员权限
            if (!IsUserAnAdmin()) {
                if (cfg.getConfig(L"start_as_admin") == L"query") {
                    DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL,
                        (DLGPROC)TestAdminProc);
                }
                else if (cfg.getConfig(L"start_as_admin") == L"always") {
                    if (RestartAdmin() == -1) {
                        MessageBox(NULL, L"获取管理员权限失败。", szTitle, MB_ICONERROR);
                    };
                }
                else {
                    return;
                }
                
            } 
            };

        switch (message) {
        case WM_CTLCOLORBTN:
        case WM_CTLCOLORSTATIC: {
            // 全局控件更改字体，下面这个是例外
            if ((HWND)lParam == hSelectKeyShow) {
                std::wstring tmp;
                wchar_t wcs[100] = {0};
                GetWindowText((HWND)lParam, wcs, 100);
                tmp = wcs;
                if (tmp != L"现在按下键盘任意按键") {
                    SetTextColor((HDC)wParam, RGB(0, 128, 0));
                } else {
                    SetTextColor((HDC)wParam, 0);
                }
                break;
            }

            SelectObject((HDC)wParam, hf);
            return (LRESULT)CreateSolidBrush(0xffffff);
            break;
        }

        case WM_CLICKDONE: { // 按键完成
            if (bAutoHide) {
                bAutoHide = false;
                ShowWindow(hWnd, SW_NORMAL);
            }
            EnableWindow(hStart, true);
            bClicking = false;
            hClickerThread = NULL;
            EnableWindow(hStop, false);
            break;
        }

        case WM_CREATE: {
            // 按键
            Clicker::KeyInfo::init_keyMap();
            // 保存全局变量
            g_hWnd = hWnd;
            bClicking = false;
            // 监测管理员并修改标题
            testForAdmin();
            if (IsUserAnAdmin()) {
                SetWindowText(hWnd, L"连点器 (管理员权限)");
            }
            // 创建字体
            hf = SimpleFont(L"微软雅黑", 18 * dpi, false);
            hfBold = SimpleFont(L"Noto Sans SC", 24 * dpi, true);

            CreateWindow(L"static",
                         L"【Shift+F2】开始或暂停点击\n【Ctrl+"
                         L"D】获取当前鼠标位置\n【Ctrl+Q】显示或隐藏程序窗口",
                         WS_CHILD | WS_VISIBLE | SS_LEFT, 12 * dpi, 320 * dpi,
                         700 * dpi, 60 * dpi, hWnd, (HMENU)1000, hInst, NULL);
            CreateWindow(L"static", L"按键", WS_CHILD | WS_VISIBLE, 12 * dpi,
                         22 * dpi, 40 * dpi, 20 * dpi, hWnd, (HMENU)1000, hInst,
                         NULL);
            hSelectKey = CreateWindow(
                L"button", L"选择按键", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                70 * dpi, 18 * dpi, 105 * dpi, 28 * dpi, hWnd,
                (HMENU)BUTTON_OPEN_KEY_DIALOG, hInst, NULL);
            CreateWindow(L"button", L"…", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                         180 * dpi, 18 * dpi, 28 * dpi, 28 * dpi, hWnd,
                         (HMENU)BUTTON_MORE_KEYS, hInst, NULL);
            hSelectKeyShow =
                CreateWindow(L"static", L"现在按下键盘任意按键",
                             WS_CHILD | WS_BORDER | SS_CENTER, 210 * dpi,
                             18 * dpi, 180 * dpi, 28 * dpi, hWnd,
                             (HMENU)BUTTON_OPEN_KEY_DIALOG, hInst, NULL);

            CreateWindow(L"static", L"连点方式",
                         WS_CHILD | WS_VISIBLE | SS_LEFT, 12 * dpi, 62 * dpi,
                         60 * dpi, 20 * dpi, hWnd, (HMENU)1000, hInst, NULL);
            CreateWindow(L"button", L"持续点按，直到手动停止",
                         WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON, 82 * dpi,
                         62 * dpi, 200 * dpi, 20 * dpi, hWnd, (HMENU)1101,
                         hInst, NULL);
            CreateWindow(L"button", L"点按一定次数后停止",
                         WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON, 82 * dpi,
                         87 * dpi, 200 * dpi, 20 * dpi, hWnd, (HMENU)1102,
                         hInst, NULL);

            CreateWindow(L"static", L"连点次数",
                         WS_CHILD | WS_VISIBLE | SS_LEFT, 12 * dpi, 122 * dpi,
                         60 * dpi, 20 * dpi, hWnd, (HMENU)1000, hInst, NULL);
            hEdTimes = CreateWindowEx(
                WS_EX_CLIENTEDGE, L"edit", L"100",
                WS_CHILD | WS_VISIBLE | ES_LEFT | ES_NUMBER, 72 * dpi,
                122 * dpi, 60 * dpi, 24 * dpi, hWnd, (HMENU)1201, hInst, NULL);
            CreateWindow(L"static", L"时间间隔",
                         WS_CHILD | WS_VISIBLE | SS_LEFT, 162 * dpi, 122 * dpi,
                         60 * dpi, 20 * dpi, hWnd, (HMENU)1000, hInst, NULL);
            hEdSleep = CreateWindowEx(
                WS_EX_CLIENTEDGE, L"edit", L"50",
                WS_CHILD | WS_VISIBLE | ES_LEFT | ES_NUMBER, 222 * dpi,
                122 * dpi, 60 * dpi, 24 * dpi, hWnd, (HMENU)1202, hInst, NULL);
            CreateWindow(L"static", L"ms", WS_CHILD | WS_VISIBLE | SS_LEFT,
                         287 * dpi, 122 * dpi, 60 * dpi, 20 * dpi, hWnd,
                         (HMENU)1000, hInst, NULL);

            hStart = CreateWindow(L"button", L"开始连点",
                                  WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
                                  12 * dpi, 400 * dpi, 100 * dpi, 24 * dpi,
                                  hWnd, (HMENU)1800, hInst, NULL);
            hStop = CreateWindow(L"button", L"停止连点",
                                 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                                 122 * dpi, 400 * dpi, 100 * dpi, 24 * dpi,
                                 hWnd, (HMENU)1801, hInst, NULL);

            CreateWindow(L"button", L"鼠标连点设置",
                         WS_CHILD | WS_VISIBLE | BS_GROUPBOX, 8 * dpi,
                         160 * dpi, 370 * dpi, 140 * dpi, hWnd,
                         (HMENU)GBOX_MOUSE, hInst, NULL);
            CreateWindow(L"button", L"将鼠标移动到指定位置",
                         WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 24 * dpi,
                         190 * dpi, 250 * dpi, 24 * dpi, hWnd,
                         (HMENU)BUTTON_MOVE_TO, hInst, NULL);
            CreateWindow(L"button", L"连点时阻止鼠标移动",
                         WS_CHILD | WS_VISIBLE | BS_CHECKBOX, 24 * dpi,
                         218 * dpi, 250 * dpi, 24 * dpi, hWnd,
                         (HMENU)BUTTON_MOUSE_LOCK, hInst, NULL);
            CreateWindow(L"static", L"鼠标位置: x=", WS_CHILD | WS_VISIBLE,
                         24 * dpi, 255 * dpi, 80 * dpi, 18 * dpi, hWnd,
                         (HMENU)2199, hInst, NULL);
            hEdPosX = CreateWindowEx(WS_EX_CLIENTEDGE, L"edit", L"",
                                     WS_CHILD | WS_VISIBLE | ES_NUMBER,
                                     113 * dpi, 252 * dpi, 64 * dpi, 24 * dpi,
                                     hWnd, (HMENU)2200, hInst, NULL);
            CreateWindow(L"static", L"y=", WS_CHILD | WS_VISIBLE, 200 * dpi,
                         255 * dpi, 40 * dpi, 18 * dpi, hWnd, (HMENU)2198,
                         hInst, NULL);
            hEdPosY = CreateWindowEx(WS_EX_CLIENTEDGE, L"edit", L"",
                                     WS_CHILD | WS_VISIBLE | ES_NUMBER,
                                     230 * dpi, 252 * dpi, 64 * dpi, 24 * dpi,
                                     hWnd, (HMENU)2201, hInst, NULL);

            hNotice = CreateWindowEx(
                WS_EX_TOPMOST | WS_EX_TOOLWINDOW, L"edit", L"TEST",
                WS_POPUP | WS_VISIBLE | SS_CENTER | WS_BORDER | ES_READONLY |
                    ES_AUTOHSCROLL,
                (GetSystemMetrics(SM_CXSCREEN) - 380 * dpi) / 2,
                GetSystemMetrics(SM_CYSCREEN) - 80 * dpi, 380 * dpi, 24 * dpi,
                NULL, NULL, hInst, NULL);

            ComboBox_AddString(hChoice, L"鼠标");
            ComboBox_AddString(hChoice, L"键盘");
            SendMessage(hChoice, WM_SETFONT, (WPARAM)hf, 0);
            SendMessage(hKey, WM_SETFONT, (WPARAM)hf, 0);
            SendMessage(hEdTimes, WM_SETFONT, (WPARAM)hf, 0);
            SendMessage(hEdSleep, WM_SETFONT, (WPARAM)hf, 0);
            SendMessage(hEdPosX, WM_SETFONT, (WPARAM)hf, 0);
            SendMessage(hEdPosY, WM_SETFONT, (WPARAM)hf, 0);
            SendMessage(hNotice, WM_SETFONT, (WPARAM)hfBold, 0);
            SendMessage(hSelectKeyShow, WM_SETFONT, (WPARAM)hfBold, 0);
            ComboBox_SetCurSel(hChoice, 0);
            Button_SetCheck(GetDlgItem(hWnd, 1102), true);
            SetFocus(hChoice);
            ShowWindow(hNotice, SW_HIDE);
            EnableWindow(hStop, false);

            hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
            SetMenu(hWnd, hMenu);
            if (IsUserAnAdmin()) {
                EnableMenuItem(GetSubMenu(hMenu, 1), IDM_ADMIN,
                               MF_DISABLED | MF_BYCOMMAND);
            }

            if (cfg.getConfig<bool>(L"load_memory")) {
                loadMemory(); // 加载记忆
            }
            break;
        }

        case WM_HOTKEY: {
            switch (wParam) {
            case HOTKEY_CLICKING: {
                // 开始或停止连点
                if (!bClicking) {
                    SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(1800, 0),
                                (LPARAM)hStart);
                    EnableWindow(hStop, true);
                } else {
                    stopSignal = true;
                }
                break;
            }
            case HOTKEY_HIDE_WINDOW: {
                // 隐藏程序窗口
                ShowWindow(hWnd, IsWindowVisible(hWnd) ? SW_HIDE : SW_SHOW);
                break;
            }
            case HOTKEY_GET_MOUSE_POS: { // 获得鼠标位置
                POINT pt = {0, 0};
                GetCursorPos(&pt);
                if (Button_GetCheck(GetDlgItem(hWnd, BUTTON_MOVE_TO))) {
                    SetWindowText(hEdPosX, std::to_wstring(pt.x).data());
                    SetWindowText(hEdPosY, std::to_wstring(pt.y).data());
                } else {
                    MessageBeep(0);
                }
                break;
            }
            }
            break;
        }
        case WM_COMMAND: {
            int wmId = LOWORD(wParam);
            static int keyCache, mouseCache;
            // 分析菜单选择:
            switch (wmId) {
                // 菜单项目
            case IDM_EXIT: {
                SendMessage(hWnd, WM_DESTROY, 0, 0);
                break;
            }
            case IDM_ADMIN: {
                saveMemory();
                if (RestartAdmin() == -1) {
                    MessageBox(hWnd, L"以管理员身份重启失败，请同意UAC对话框！",
                               szTitle, MB_ICONSTOP);
                }
                break;
            }
            case IDM_HIDE_WINDOW: {
                SendMessage(hWnd, WM_HOTKEY, 2, 0);
                break;
            }
            case IDM_ABOUT: {
                // 未完成
                break;
            }
            case IDM_PREF: {
                DialogBoxParam(hInst, MAKEINTRESOURCE(SETTINGS_DIALOG), hWnd,
                               (DLGPROC)SettingsProc, (LPARAM)&cfg);
                // 未完成
                break;
            }
            case IDM_GUIDEPAGE: { // 打开README.md（之后应该会写一个更好的使用说明）
                WinExec(
                    "cmd /c start "
                    "https://gitee.com/emmm-233/clicker/blob/master/README.md",
                    SW_HIDE);
                break;
            }

                // 点按设置 Radio Button
            case 1101: {
                Button_SetCheck(GetDlgItem(hWnd, 1101), true);
                Button_SetCheck(GetDlgItem(hWnd, 1102), false);
                EnableWindow(hEdTimes, false);
                SetWindowText(hEdTimes, L"");
                break;
            }
            case 1102: {
                Button_SetCheck(GetDlgItem(hWnd, 1102), true);
                Button_SetCheck(GetDlgItem(hWnd, 1101), false);
                EnableWindow(hEdTimes, true);
                break;
            }

                // 选择按键
            case BUTTON_OPEN_KEY_DIALOG: {
                bSelectingKey = true;
                ShowWindow(hSelectKeyShow, SW_NORMAL);
                SetWindowText(hSelectKeyShow, L"现在按下键盘任意按键");
                SetFocus(hWnd);
                Button_SetState((HWND)lParam, true);

                break;
            }
            case BUTTON_MORE_KEYS: { // 更多
                HMENU hPop = CreatePopupMenu();
                HMENU hMore = CreatePopupMenu();
                AppendMenu(hPop, MF_STRING, VK_LWIN, L"Windows 徽标键");
                AppendMenu(hPop, MF_STRING, VK_LBUTTON, L"鼠标左键");
                AppendMenu(hPop, MF_STRING, VK_RBUTTON, L"鼠标右键");
                AppendMenu(hPop, MF_SEPARATOR, 0, 0);
                AppendMenu(hPop, MF_POPUP, (UINT_PTR)hMore, L"所有可用按键");
                for (const auto &pair : Clicker::KeyInfo::keyMap) {
                    if (pair.first >= 0)
                        AppendMenu(hMore, MF_STRING, pair.first,
                                   pair.second.data());
                }
                POINT pt;
                GetCursorPos(&pt);
                TrackPopupMenu(hPop, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hWnd,
                               nullptr);
                DestroyMenu(hPop);
                DestroyMenu(hMore);
                break;
            }
            case BUTTON_MOVE_TO: { // 是否打开移动到指定位置
                bool check = Button_GetCheck((HWND)lParam);
                if (!check) {
                    Button_SetCheck(GetDlgItem(hWnd, BUTTON_MOUSE_LOCK), false);
                    EnableWindow(GetDlgItem(hWnd, BUTTON_MOUSE_LOCK), false);
                    EnableWindow(hEdPosX, false);
                    EnableWindow(hEdPosY, false);
                    SetWindowText(hEdPosX, L"");
                    SetWindowText(hEdPosY, L"");
                } else {
                    EnableWindow(GetDlgItem(hWnd, BUTTON_MOUSE_LOCK), true);
                    EnableWindow(hEdPosX, true);
                    EnableWindow(hEdPosY, true);
                }
                break;
            }
            case BUTTON_MOUSE_LOCK: {
                static bool tip;
                if (!Button_GetCheck((HWND)lParam)) {
                    if (!tip && cfg.getConfig<bool>(L"show_warnings")) {
                        if (MessageBox(
                                hWnd,
                                L"注意：此操作将会使连点期间鼠标始终固定在"
                                L"指定的坐标处，无法移动。请谨慎使用。",
                                szTitle,
                                MB_ICONWARNING | MB_OKCANCEL) == IDOK) {
                            tip = true; // 开一次程序只提醒一次
                            Button_SetCheck((HWND)lParam, true);
                        }
                        break;
                    }
                    Button_SetCheck((HWND)lParam, true);
                } else {
                    Button_SetCheck((HWND)lParam, false);
                }
                break;
            }
            case BUTTON_GET_MOUSE_POS: {

                break;
            }
            case 2200: { // 鼠标位置x
                int sx = GetSystemMetrics(SM_CXSCREEN);
                int x_tmp = 0;
                getWindowText_asInt((HWND)lParam, &x_tmp);
                if (x_tmp > sx) {
                    SetWindowText((HWND)lParam, std::to_wstring(sx).data());
                    MessageBeep(0);
                }
                break;
            }
            case 2201: { // 鼠标位置y
                int sy = GetSystemMetrics(SM_CYSCREEN);
                int y_tmp = 0;
                getWindowText_asInt((HWND)lParam, &y_tmp);
                if (y_tmp > sy) {
                    SetWindowText((HWND)lParam, std::to_wstring(sy).data());
                    MessageBeep(0);
                }
                break;
            }
            case 1201: { // 次数
                int times_tmp = 100;
                getWindowText_asInt((HWND)lParam, &times_tmp);
                if (times_tmp > 100000) {
                    SetWindowText((HWND)lParam, std::to_wstring(100000).data());
                    MessageBeep(0);
                }
                break;
            }
                //  按下确定开始
            case 1800: {
                int iTimes = 0, iSleep = 0;
                bool inf = false;
                bool bMoveToPos = false, bLockMouse = false;
                bool show = cfg.getConfig<bool>(L"notice_window");
                bool bWarn = cfg.getConfig<bool>(L"show_warnings");
                POINT pos;
                if (IsWindowEnabled(hEdTimes)) {
                    if (GetWindowTextLength(hEdTimes) < 1) {
                        MessageBox(hWnd, L"请先将配置参数填写完整。", L"错误",
                                   MB_ICONSTOP);
                        break;
                    }
                    if (GetWindowTextLength(hEdSleep) < 1) {
                        MessageBox(hWnd, L"请先将配置参数填写完整。", L"错误",
                                   MB_ICONSTOP);
                        break;
                    }
                } else {
                    if (GetWindowTextLength(hEdSleep) < 1) {
                        MessageBox(hWnd, L"请先将配置参数填写完整。", L"错误",
                                   MB_ICONSTOP);
                        inf = true;
                        break;
                    }
                }
                wchar_t sTimes[100] = {0}, sSleep[100] = {0};
                GetWindowText(hEdSleep, sSleep, 100);
                if (!inf) {
                    GetWindowText(hEdTimes, sTimes, 100);
                    swscanf_s(sTimes, L"%d", &iTimes);
                } else {
                    iTimes = -1;
                }
                GetWindowText(hEdSleep, sSleep, 100);
                swscanf_s(sSleep, L"%d", &iSleep);

                if (key.getID() < 0) {
                    MessageBox(hWnd, L"请先选择按键。", L"错误", MB_ICONSTOP);
                    break;
                }
                if (key.getType() == Clicker::KeyInfo::mouse &&
                    key.getMouseEvent().empty()) {
                    MessageBox(hWnd, L"暂时还不支持当前鼠标按键。", L"连点器",
                               MB_ICONINFORMATION);
                    break;
                }

                // 检测是否需要移动鼠标
                bMoveToPos = Button_GetCheck(GetDlgItem(hWnd, BUTTON_MOVE_TO));
                if (bMoveToPos) {
                    if (GetWindowTextLength(hEdPosX) < 1 ||
                        GetWindowTextLength(hEdPosY) < 1) {
                        MessageBox(hWnd, L"请先将配置参数填写完整。", L"错误",
                                   MB_ICONSTOP);
                        break;
                    }
                }
                bLockMouse =
                    Button_GetCheck(GetDlgItem(hWnd, BUTTON_MOUSE_LOCK));
                getWindowText_asInt(hEdPosX, (int *)&pos.x);
                getWindowText_asInt(hEdPosY, (int *)&pos.y);

                click.ModifyConfig(show,iTimes, iSleep, key);
                click.SetPosConfig(bMoveToPos, bLockMouse, pos);

                if (bLockMouse && bWarn) {
                    wstring warning = L"";
                    if (iTimes > 0) {
                        warning =
                            L"即将连点 \"" + key.getStr() + L"\" " +
                            std::to_wstring(iTimes) + L" 次，预计总耗时 " +
                            std::to_wstring((double)iTimes * iSleep / 1000) +
                            L" 秒。\n" +
                            L"你已设置连点时禁止鼠标移动，如需要中止连点请记住"
                            L"快捷键【Shift+F2】。\n\n" +
                            L"是否继续？";
                    } else {
                        warning =
                            L"即将连点 \"" + key.getStr() +
                            L"\"。\n请注意，你已同时设定"
                            L"连点时禁止鼠标移动和持续点击，这意味着连点"
                            L"在达到上限次数 (100,000次) "
                            L"前无法停止，可能会持续很长时间！如需要中止连点"
                            L"请记住快捷键【Shift+F2】。\n\n" +
                            L"是否继续？";
                    }

                    if (MessageBox(hWnd, warning.data(), szTitle,
                                   MB_ICONWARNING | MB_YESNO) == IDNO) {
                        break;
                    }
                }

                // 自动隐藏窗口
                // 需要按下时窗口是显示出来的，而不是用户已经将其隐藏
                if (IsWindowVisible(hWnd)) { 
                    if (cfg.getConfig<bool>(L"hide_when_clicking")) {
                        bAutoHide = true;
                        ShowWindow(hWnd, SW_HIDE);
                    }
                }

                hClickerThread =
                    CreateThread(NULL, 0, ClickerThread, &click, 0, NULL);
                EnableWindow((HWND)lParam, false);
                EnableWindow(hStop, true);
                bClicking = true;

                break;
            }
            case 1801: {
                SendMessage(hWnd, WM_HOTKEY, 1, 0);
                break;
            }
            default:
                break;
            }
            if (wParam >= 0x01 && wParam <= 0xfe) {
                bSelectingKey = true;
                SendMessage(hWnd, WM_KEYUP, wParam, 0);
                break;
            }
            return DefWindowProc(hWnd, message, wParam, lParam);
        } break;
        case WM_KILLFOCUS: {
            bSelectingKey = false;
            Button_SetState(hSelectKey, false);
            ShowWindow(hSelectKeyShow, SW_HIDE);
            if (key.getID() > 0) {
                std::wstring text = L"已选择: " + key.getStr();
                SetWindowText(hSelectKey, text.data());
            } else {
                SetWindowText(hSelectKey, L"选择按键");
            }
            break;
        }
        case WM_SYSKEYUP:
        case WM_KEYUP: {
            if (bSelectingKey == true) {
                key.modify_vkeyID(wParam);
                SetWindowText(hSelectKeyShow, key.getStr().data());
                Button_SetState(hSelectKey, false);
                bSelectingKey = false;
                if (key.getID() > 0) {
                    std::wstring text = L"已选择: " + key.getStr();
                    SetWindowText(hSelectKey, text.data());
                    if (key.getType() == Clicker::KeyInfo::mouse) {
                        SetWindowText(GetDlgItem(hWnd, GBOX_MOUSE),
                                      L"鼠标连点设置");
                        EnableWindow(GetDlgItem(hWnd, BUTTON_MOVE_TO), true);
                    } else {
                        SetWindowText(GetDlgItem(hWnd, GBOX_MOUSE),
                                      L"鼠标连点设置 (仅当按键为鼠标键时生效)");
                        Button_SetCheck(GetDlgItem(hWnd, BUTTON_MOVE_TO),
                                        false);
                        EnableWindow(GetDlgItem(hWnd, BUTTON_MOVE_TO), false);
                        SendMessage(hWnd, WM_COMMAND, BUTTON_MOVE_TO,
                                    (LPARAM)GetDlgItem(hWnd, BUTTON_MOVE_TO));
                    }
                }
            }

            break;
        }
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        } break;

        case WM_DESTROY: {
            saveMemory();
            PostQuitMessage(0);
            break;
        }
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    } // 错误处理
    catch (nlohmann::json_abi_v3_12_0::detail::parse_error& pse) {
        tackleCfgError(pse);
    } catch (nlohmann::json_abi_v3_12_0::detail::out_of_range& ofr) {
        tackleCfgError(ofr);
    } catch (std::exception& exc) {
        MessageBox(nullptr, Clicker::s2w(exc.what()).data(),
                   L"连点器 - 程序出现错误", MB_ICONSTOP);
        exit(114);
    }
}

// 管理员消息提示
BOOL CALLBACK TestAdminProc(HWND hDlg, UINT message, WPARAM wParam,
                            LPARAM lParam) {
    switch (message) {
    case WM_INITDIALOG: {
        return true;
    }
    case WM_COMMAND: {
        switch (LOWORD(wParam)) {
        case IDOK2: {
            exit(0);
            break;
        }
        case IDOK3: {
            EndDialog(hDlg, 0);
            break;
        }
        case IDOK4: {
            if (RestartAdmin() == -1) {
                MessageBox(hDlg, L"以管理员身份重启失败，请同意UAC！", L"错误",
                           MB_ICONSTOP);
            }
        }
        }
        break;
    }
    case WM_CLOSE: {
        EndDialog(hDlg, 0);
        return true;
    }
    default: {
        return false;
    }
    }
    return false;
}
// 设置
BOOL CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam,
                           LPARAM lParam) {
    static HWND hCBox_Admin, hBtn_Memo, hBtn_Warn, hBtn_Notice, hBtn_Hide;
    static Clicker::JsonConfig* ptrCfg;
    extern const std::wstring adminActions[3];

    // Lambda 表达式：加载和保存配置
    auto loadConfig = [&]() {
        ptrCfg = (Clicker::JsonConfig*)lParam; //通过指针传递配置信息
        for (int i = 0; i <= 2; i++) {
            if (adminActions[i] == ptrCfg->getConfig(L"start_as_admin")) {
                ComboBox_SetCurSel(hCBox_Admin, i);
            };
        }
        Button_SetCheck(hBtn_Memo, ptrCfg->getConfig<bool>(L"load_memory"));
        Button_SetCheck(hBtn_Warn, ptrCfg->getConfig<bool>(L"show_warnings"));
        Button_SetCheck(hBtn_Notice, ptrCfg->getConfig<bool>(L"notice_window"));
        Button_SetCheck(hBtn_Hide, ptrCfg->getConfig<bool>(L"hide_when_clicking"));
        };
    auto saveConfig = [&]() {
        ptrCfg->setConfig(L"start_as_admin", adminActions[ComboBox_GetCurSel(hCBox_Admin)]);
        ptrCfg->setConfig<bool>(L"show_warnings", Button_GetCheck(hBtn_Warn));
        ptrCfg->setConfig<bool>(L"notice_window", Button_GetCheck(hBtn_Notice));
        ptrCfg->setConfig<bool>(L"load_memory", Button_GetCheck(hBtn_Memo));
        ptrCfg->setConfig<bool>(L"hide_when_clicking", Button_GetCheck(hBtn_Hide));
        ptrCfg->saveConfig(); // 同时写入磁盘
        };

    switch (message) {
    case WM_INITDIALOG: {
        hCBox_Admin = GetDlgItem(hDlg, IDC_ADMIN_COMBO);
        hBtn_Memo = GetDlgItem(hDlg, IDC_REMEMBER_SETTINGS);
        hBtn_Notice = GetDlgItem(hDlg, IDC_SHOW_INFO);
        hBtn_Warn = GetDlgItem(hDlg, IDC_LOCK_WARNING);
        hBtn_Hide = GetDlgItem(hDlg, IDC_HIDE_WINDOW);
        ComboBox_AddString(hCBox_Admin, L"没有管理员权限时询问");
        ComboBox_AddString(hCBox_Admin, L"始终尝试以管理员权限运行");
        ComboBox_AddString(hCBox_Admin, L"不自动以管理员权限运行");
        ComboBox_SetCurSel(hCBox_Admin, 0);
        loadConfig();
        
        //快捷键设置还未完成
        ShowWindow(GetDlgItem(hDlg, IDC_HOTKEY_BUTTON), SW_HIDE);

        return true;
    }
    case WM_COMMAND: {
        switch (wParam) {
        case IDOK: {
            saveConfig();
            __fallthrough;
        }
        case IDCANCEL: {
            EndDialog(hDlg,0);
            return true;
        }
        }
        break;
    }
    default: {
        return false;
    }
    }
}